package com.seed.core.query;

import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.mybatisflex.core.query.QueryColumn;
import com.mybatisflex.core.query.QueryCondition;
import com.mybatisflex.core.query.QueryTable;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.row.Db;
import com.mybatisflex.core.row.Row;
import com.mybatisflex.core.row.RowUtil;
import com.seed.core.enumeration.Direction;
import com.seed.core.pojo.Const;
import com.seed.core.pojo.Join;
import com.seed.core.pojo.OrderBy;
import com.seed.core.pojo.seed.Habtms;
import com.seed.core.pojo.seed.HasManys;
import com.seed.core.pojo.seed.Seed;
import com.seed.core.query.pojo.Declared;
import com.seed.core.query.pojo.Ordered;
import com.seed.core.query.pojo.Searched;
import com.seed.util.RowKit;
import com.seed.util.StrKit;
import com.seed.util.SeedKit;

import java.util.*;

public class SeedFill {
    private final String basePath; // self对象的路径
    private final String cross; // self对象下要填充的字段名称
    private final Seed seed; // 要填充的字段对应的seed
    private final SeedDeclared seedDeclared;
    private final HasManys hasMany;
    private SeedSearched seedSearched;
    private SeedKeyword seedKeyword;
    private SeedOrdered seedOrdered;
    private SeedDataPermied seedDataPermied;

    public SeedFill(String basePath, String cross, HasManys hasMany, Seed seed, Declared declared) {
        this.basePath = basePath;
        this.cross = cross;
        this.seed = seed;
        this.hasMany = hasMany;
        this.seedDeclared = new SeedDeclared(seed, declared);
    }

    public void setSearched(Searched searched) {
        this.seedSearched = new SeedSearched(seed, searched);
    }

    public void setKeyword(String keyword, Declared declared) {
        this.seedKeyword = new SeedKeyword(keyword, seed, declared);
    }

    public void setDataPermied(List<List<List<Map<String, Searched>>>> dataPermied) {
        this.seedDataPermied = new SeedDataPermied(seed, dataPermied);
    }

    public void setOrdered(Ordered ordered) {
        this.seedOrdered = new SeedOrdered(seed, ordered);
    }

    public QueryWrapper getQueryWrapper() {
        QueryTable table = new QueryTable(seed.getTableName()).as("t");
        QueryWrapper qw = QueryWrapper.create().from(table);

        Map<String, QueryColumn> selects = seedDeclared.getSelects();
        if(selects.isEmpty()) {
            QueryColumn[] columns = SeedKit.getDefaultDeclares(seed)
                    .stream()
                    .map(columnName -> new QueryColumn(table, columnName))
                    .toList()
                    .toArray(new QueryColumn[0]);
            qw.select(columns);
        } else {
            qw.select(selects.values().toArray(new QueryColumn[0]));
        }

        Map<String, Join> joins = new HashMap<>();
        for(Map.Entry<String, Join> entry: seedDeclared.getLeftJoins().entrySet()) {
            joins.putIfAbsent(entry.getKey(), entry.getValue());
        }
        if(seedSearched != null) {
            for(Map.Entry<String, Join> entry: seedSearched.getJoins().entrySet()) {
                joins.putIfAbsent(entry.getKey(), entry.getValue());
            }
        }
        if(seedKeyword != null) {
            for(Map.Entry<String, Join> entry: seedKeyword.getJoins().entrySet()) {
                joins.putIfAbsent(entry.getKey(), entry.getValue());
            }
        }
        if(seedDataPermied != null) {
            for(Map.Entry<String, Join> entry: seedDataPermied.getJoins().entrySet()) {
                joins.putIfAbsent(entry.getKey(), entry.getValue());
            }
        }
        if(seedOrdered != null) {
            for(Map.Entry<String, Join> entry: seedOrdered.getLeftJoins().entrySet()) {
                joins.putIfAbsent(entry.getKey(), entry.getValue());
            }
        }

        for(Join leftJoin: joins.values()) {
            qw.leftJoin(leftJoin.getTable()).on(leftJoin.getOn());
        }

        Map<String, SeedInCondition> inQuerys = new LinkedHashMap<>();
        if(seedSearched != null) inQuerys.putAll(seedSearched.getInQuerys());
        if(seedKeyword != null) {
            for(Map.Entry<String, SeedInCondition> entry: seedKeyword.getInQuerys().entrySet()) {
                String key = entry.getKey();
                SeedInCondition value = entry.getValue();
                SeedInCondition seedInCondition = inQuerys.get(key);
                if(seedInCondition == null) {
                    inQuerys.putIfAbsent(key, value);
                } else {
                    seedInCondition.setDeclared(value.getDeclared());
                }
            }
        }
        if(seedDataPermied != null) {
            for(Map.Entry<String, SeedInCondition> entry: seedDataPermied.getInQuerys().entrySet()) {
                String key = entry.getKey();
                SeedInCondition value = entry.getValue();
                SeedInCondition seedInCondition = inQuerys.get(key);
                if(seedInCondition == null) {
                    inQuerys.put(key, value);
                } else {
                    seedInCondition.setDataPermied(value.getDataPermied());
                }
            }
        }

        for(Map.Entry<String, SeedInCondition> entry: inQuerys.entrySet()) {
            qw.and(entry.getValue().getQueryCondition());
        }

        if(seedSearched != null) {
            for(QueryCondition condition: seedSearched.getWheres()) qw.and(condition);
        }

        if(seedKeyword != null) {
            qw.and(queryWrapper -> {
                for(QueryCondition condition: seedKeyword.getWheres()) queryWrapper.or(condition);
            });
        }
        if(seedDataPermied != null) qw.and(seedDataPermied.getQueryCondition());
        if(seedOrdered != null) {
            for(OrderBy orderBy: seedOrdered.getOrders().values()) {
                qw.orderBy(orderBy.getColumn(), orderBy.getDirection().equals(Direction.asc));
            }
        }

        return qw;
    }

    public void fills(List<?> rootList) {
        for(Map.Entry<String, SeedFill> entry: seedDeclared.getFills().entrySet()) {
            String key = entry.getKey();
            SeedFill seedFill = entry.getValue();
            if(seedSearched != null) {
                Searched searched = seedSearched.getFillSearched(key);
                if(searched != null) seedFill.setSearched(searched);
            }
            if(seedKeyword != null) {
                String keyword = seedKeyword.getKeyword();
                Declared declared = seedKeyword.getFillKeyworded(key);
                if(StrUtil.isNotBlank(keyword) && declared != null) {
                    seedFill.setKeyword(keyword, declared);
                }
            }
            if(seedDataPermied != null) {
                List<List<List<Map<String, Searched>>>> dataPermied = seedDataPermied.getFillDataPermied();
                if(dataPermied != null) seedFill.setDataPermied(dataPermied);
            }
            if(seedOrdered != null) {
                Ordered ordered = seedOrdered.getFillOrdered(key);
                if(ordered != null) seedFill.setOrdered(ordered);
            }

            entry.getValue().fill(rootList);
        }
    }

    public void fill(List<?> rootList) {
//        if(hasMany instanceof Habtms) {
//            fillHabtm(rootList);
//        } else {
//            fillHasMany(rootList);
//        }
    }

//    private void fillHasMany(List<?> rootList) {
//        if(rootList.isEmpty()) return ;
//
//        Class<?> targetEntityClass = hasMany.getTargetEntityClass();
//        if(targetEntityClass == null) {
//            fillHasManyForRow(rootList);
//        } else {
//            fillHasManyForObject(rootList);
//        }
//    }

//    private void fillHasManyForRow(List<?> rootList) {
//        List<?> selfList = RowKit.getSelfList(rootList, basePath);
//        String selfColumnName = hasMany.getSelfColumnName();
//        String fieldName = StrUtil.toCamelCase(cross);
//        Integer limit = hasMany.getLimit();
//        String targetTableAlias = Const.MAIN_TABLE_ALIAS;
//
//        Set<String> selfConditionColumnNames = hasMany.getConditionColumnNames(TableType.self);
//        if(StrUtil.isNotBlank(selfColumnName) && selfConditionColumnNames !=null && selfConditionColumnNames.isEmpty()) {
//            /**
//             * 有关联字段并且关联条件里不带self列, 统一查询，然后再根据关联字段分配给每一个self对象
//             */
//            QueryCondition condition = hasMany.getQueryCondition(selfList, targetTableAlias);
//            QueryWrapper qw = getQueryWrapper().where(condition).limit(limit);
//            List<Row> targetList = Db.selectListByQuery(qw);
//            fills(targetList);
//
//            String selfField = StrUtil.toCamelCase(selfColumnName);
//            String targetField = StrUtil.toCamelCase(hasMany.inferTargetColumn());
//            for(Object self: selfList) {
//                Row row = (Row) self;
//                List<Row> targetListForSelf = targetList
//                        .stream()
//                        .filter(target -> target.get(targetField).equals(row.get(selfField)))
//                        .toList();
//                row.set(fieldName, targetListForSelf);
//            }
//        } else {
//            /**
//             * 每条数据的子数据单独查询
//             */
//            for(Object self: selfList) {
//                Row row = (Row) self;
//                QueryCondition condition = hasMany.getQueryCondition(self, targetTableAlias);
//                QueryWrapper qw = getQueryWrapper().where(condition).limit(limit);
//                List<Row> targetList = Db.selectListByQuery(qw);
//                fills(targetList);
//                row.set(fieldName, targetList);
//            }
//        }
//    }

    private void fillHasManyForRow(List<?> rootList) {
//        List<?> selfList = RowKit.getSelfList(rootList, basePath);
//        Integer limit = hasMany.getLimit();
//        String targetTableAlias = Const.MAIN_TABLE_ALIAS;
//        String fieldName = StrUtil.toCamelCase(cross);
//
//        /**
//         * 统一查询，然后再根据关联字段分配给每一个self对象
//         */
//        QueryCondition condition = hasMany.getQueryCondition(selfList, targetTableAlias);
//        QueryWrapper qw = getQueryWrapper().where(condition).limit(limit);
//        List<Row> targetList = Db.selectListByQuery(qw);
//        fills(targetList);
//
//        String selfField = StrUtil.toCamelCase(hasMany.inferSelfColumn());
//        String targetField = StrUtil.toCamelCase(hasMany.inferTargetColumn());
//        for(Object self: selfList) {
//            Row row = (Row) self;
//            List<Row> targetListForSelf = targetList
//                    .stream()
//                    .filter(target -> target.get(targetField).equals(row.get(selfField)))
//                    .toList();
//            row.set(fieldName, targetListForSelf);
//        }

//        if(hasMany.getExtraType() == ExtraType.code) {
//            /**
//             * 统一查询，然后再根据关联字段分配给每一个self对象
//             */
//            QueryCondition condition = hasMany.getQueryCondition(selfList, targetTableAlias);
//            QueryWrapper qw = getQueryWrapper().where(condition).limit(limit);
//            List<Row> targetList = Db.selectListByQuery(qw);
//            fills(targetList);
//
//            String codeField = StrUtil.toCamelCase(hasMany.getCodeColumnName());
//            for(Object self: selfList) {
//                Row row = (Row) self;
//                List<Row> targetListForSelf = targetList
//                        .stream()
//                        .filter(target -> {
//                            String targetCodeValue = target.getString(codeField);
//                            String selfCodeValue = targetCodeValue.substring(0, targetCodeValue.lastIndexOf(Const.CODE_SEPARATOR));
//                            return selfCodeValue.equals(row.get(codeField));
//                        }).toList();
//                row.set(fieldName, targetListForSelf);
//            }
//        } else {
//            /**
//             * 统一查询，然后再根据关联字段分配给每一个self对象
//             */
//            QueryCondition condition = hasMany.getQueryCondition(selfList, targetTableAlias);
//            QueryWrapper qw = getQueryWrapper().where(condition).limit(limit);
//            List<Row> targetList = Db.selectListByQuery(qw);
//            fills(targetList);
//
//            String selfField = StrUtil.toCamelCase(hasMany.inferSelfColumn());
//            String targetField = StrUtil.toCamelCase(hasMany.inferTargetColumn());
//            for(Object self: selfList) {
//                Row row = (Row) self;
//                List<Row> targetListForSelf = targetList
//                        .stream()
//                        .filter(target -> target.get(targetField).equals(row.get(selfField)))
//                        .toList();
//                row.set(fieldName, targetListForSelf);
//            }
//        }
    }

    private void fillHasManyForObject(List<?> rootList) {
//        List<?> selfList = RowKit.getSelfList(rootList, basePath);
//        Integer limit = hasMany.getLimit();
//        String targetTableAlias = Const.MAIN_TABLE_ALIAS;
//        String fieldName = StrUtil.toCamelCase(cross);
//
//        QueryCondition condition = hasMany.getQueryCondition(selfList, targetTableAlias);
//        QueryWrapper qw = getQueryWrapper().where(condition).limit(limit);
//        List<?> targetList = RowUtil.toEntityList(Db.selectListByQuery(qw), hasMany.getTargetEntityClass());
//        fills(targetList);
//
//        String selfField = StrUtil.toCamelCase(hasMany.inferSelfColumn());
//        String targetField = StrUtil.toCamelCase(hasMany.inferTargetColumn());
//        for(Object self: selfList) {
//            List<?> targetListForSelf = targetList
//                    .stream()
//                    .filter(target -> ReflectUtil.getFieldValue(target, targetField).equals(ReflectUtil.getFieldValue(self, selfField)))
//                    .toList();
//            ReflectUtil.setFieldValue(self, fieldName, targetListForSelf);
//        }

//        if(hasMany.getExtraType() == ExtraType.code) {
//            /**
//             * 统一查询，然后再根据关联字段分配给每一个self对象
//             */
//            QueryCondition condition = hasMany.getQueryCondition(selfList, targetTableAlias);
//            QueryWrapper qw = getQueryWrapper().where(condition).limit(limit);
//            List<?> targetList = RowUtil.toEntityList(Db.selectListByQuery(qw), hasMany.getTargetEntityClass());
//            fills(targetList);
//
//            String codeField = StrUtil.toCamelCase(hasMany.getCodeColumnName());
//            for(Object self: selfList) {
//                List<?> targetListForSelf = targetList
//                        .stream()
//                        .filter(target -> {
//                            String targetCodeValue = (String) ReflectUtil.getFieldValue(target, codeField);
//                            String selfCodeValue = targetCodeValue.substring(0, targetCodeValue.lastIndexOf(Const.CODE_SEPARATOR));
//                            return selfCodeValue.equals(ReflectUtil.getFieldValue(self, codeField));
//                        }).toList();
//                ReflectUtil.setFieldValue(self, fieldName, targetListForSelf);
//            }
//        } else {
//            QueryCondition condition = hasMany.getQueryCondition(selfList, targetTableAlias);
//            QueryWrapper qw = getQueryWrapper().where(condition).limit(limit);
//            List<?> targetList = RowUtil.toEntityList(Db.selectListByQuery(qw), hasMany.getTargetEntityClass());
//            fills(targetList);
//
//            String selfField = StrUtil.toCamelCase(hasMany.inferSelfColumn());
//            String targetField = StrUtil.toCamelCase(hasMany.inferTargetColumn());
//            for(Object self: selfList) {
//                List<?> targetListForSelf = targetList
//                        .stream()
//                        .filter(target -> ReflectUtil.getFieldValue(target, targetField).equals(ReflectUtil.getFieldValue(self, selfField)))
//                        .toList();
//                ReflectUtil.setFieldValue(self, fieldName, targetListForSelf);
//            }
//        }
    }

//    private void fillHasManyForObject(List<?> rootList) {
//        List<?> selfList = RowKit.getSelfList(rootList, basePath);
//        String selfColumnName = hasMany.getSelfColumnName();
//        String fieldName = StrUtil.toCamelCase(cross);
//        Integer limit = hasMany.getLimit();
//        String targetTableAlias = "t";
//
//        Set<String> selfConditionColumnNames = hasMany.getConditionColumnNames(TableType.self);
//        if(StrUtil.isNotBlank(selfColumnName) && selfConditionColumnNames.isEmpty()) {
//            /**
//             * 有关联字段并且关联条件里不带self列, 统一查询，然后再根据关联字段分配给每一个self对象
//             */
//            QueryCondition condition = hasMany.getQueryCondition(selfList, targetTableAlias);
//            QueryWrapper qw = getQueryWrapper().where(condition).limit(limit);
//            List<?> targetList = RowUtil.toEntityList(Db.selectListByQuery(qw), hasMany.getTargetEntityClass());
//            fills(targetList);
//
//            String selfField = StrUtil.toCamelCase(selfColumnName);
//            String targetField = StrUtil.toCamelCase(hasMany.inferTargetColumn());
//            for(Object self: selfList) {
//                List<?> targetListForSelf = targetList
//                        .stream()
//                        .filter(target -> ReflectUtil.getFieldValue(target, targetField).equals(ReflectUtil.getFieldValue(self, selfField)))
//                        .toList();
//                ReflectUtil.setFieldValue(self, fieldName, targetListForSelf);
//            }
//        } else {
//            /**
//             * 每条数据的子数据单独查询
//             */
//            for(Object self: selfList) {
//                QueryWrapper qw = getQueryWrapper().where(hasMany.getQueryCondition(self, targetTableAlias)).limit(limit);
//                List<?> targetList = RowUtil.toEntityList(Db.selectListByQuery(qw), hasMany.getTargetEntityClass());
//                fills(targetList);
//                ReflectUtil.setFieldValue(self, fieldName, targetList);
//            }
//        }
//    }

    private void fillHabtm(List<?> rootList) {
//        if(rootList.isEmpty()) return ;
//
//        Class<?> targetEntityClass = hasMany.getTargetEntityClass();
//        if(targetEntityClass == null) {
//            fillHabtmForRow(rootList);
//        } else {
//            fillHabtmForObject(rootList);
//        }
    }

    private void fillHabtmForRow(List<?> rootList) {
//        List<?> selfList = RowKit.getSelfList(rootList, basePath);
//        String fieldName = StrUtil.toCamelCase(cross);
//        Habtms habtm = (Habtms) hasMany;
//        String through = habtm.throughTableName();
//        Integer limit = hasMany.getLimit();
//        String throughAlias = "_";
//        for(Object self: selfList) {
//            Row row = (Row) self;
//            QueryCondition throughCondition = habtm.getThroughQueryCondition(self, throughAlias);
//            QueryWrapper qw = getQueryWrapper().innerJoin(new QueryTable(through).as(throughAlias)).on(throughCondition).limit(limit);
//            QueryCondition condition = habtm.getQueryCondition(self, throughAlias, "t");
//            List<Row> targetList = Db.selectListByQuery(qw.where(condition));
//            fills(targetList);
//            row.set(fieldName, targetList);
//        }
    }

    private void fillHabtmForObject(List<?> rootList) {
        List<?> selfList = RowKit.getSelfList(rootList, basePath);
        String fieldName = StrUtil.toCamelCase(cross);
        Habtms habtm = (Habtms) hasMany;
        String through = habtm.throughTableName();
        Integer limit = hasMany.getLimit();
        String throughAlias = StrKit.throughAlias(basePath, cross);

        String inferThroughSelfColumn = habtm.inferThroughSelfColumn();
        String inferThroughTargetColumn = habtm.inferThroughTargetColumn();
        Class<?> targetEntityClass = habtm.getTargetEntityClass();
//        for(Object self: selfList) {
//            QueryCondition throughCondition = habtm.getThroughQueryCondition1(Const.MAIN_TABLE_ALIAS, throughAlias);
//            QueryWrapper qw = getQueryWrapper().innerJoin(new QueryTable(through).as(throughAlias)).on(throughCondition).limit(limit);
//            QueryCondition condition = habtm.getQueryCondition(self, throughAlias, Const.MAIN_TABLE_ALIAS);
//            List<?> targetList = RowUtil.toEntityList(Db.selectListByQuery(qw.where(condition)), habtm.getTargetEntityClass());
//            fills(targetList);
//            ReflectUtil.setFieldValue(self, fieldName, targetList);
//        }

//        QueryTable throughTable = new QueryTable(through).as(throughAlias);
//        QueryCondition throughCondition = habtm.getThroughQueryCondition1(Const.MAIN_TABLE_ALIAS, throughAlias);
//        QueryWrapper qw = getQueryWrapper().innerJoin(throughTable).on(throughCondition).limit(limit);
//        QueryCondition condition = habtm.getQueryCondition(selfList, throughAlias, Const.MAIN_TABLE_ALIAS);
//
//        // 添加中间表列
//        qw.select(new QueryColumn(throughTable, inferThroughSelfColumn));
//        qw.select(new QueryColumn(throughTable, inferThroughTargetColumn));
//
//        List<Row> targetList = Db.selectListByQuery(qw.where(condition));
//        Map<Object, List<Object>> map = new LinkedHashMap<>();
//        // 按中间表列把目标数据分组
//        for(Row targetRow: targetList) {
//            Object throughSelfValue = targetRow.get(inferThroughSelfColumn);
//            List<Object> list = map.get(throughSelfValue);
//            if(list == null) list = new ArrayList<>();
//            list.add(RowUtil.toEntity(targetRow, targetEntityClass));
//            map.put(throughSelfValue, list);
//        }
//
//        String selfField = StrUtil.toCamelCase(habtm.inferSelfColumn());
//        for(Object self: selfList) {
//            Object selfValue = ReflectUtil.getFieldValue(self, selfField);
//            List<Object> targetListForSelf = map.get(selfValue);
//            if(targetListForSelf == null || targetListForSelf.isEmpty()) continue;
//
//            fills(targetListForSelf);
//            ReflectUtil.setFieldValue(self, fieldName, targetListForSelf);
//        }
    }
}
